;
;  PROGRAM:  PRINT III (PRINT3)
;  VERSION:  1.2
;  DATE:  26 Apr 83
;  AUTHOR:  RICHARD CONN
;  PREVIOUS VERSIONS:  1.1 (26 Apr 83), 1.0 (22 Apr 83)
;
VERS	equ	12

FALSE	equ	0
TRUE	equ	NOT FALSE

timeok	equ	FALSE	;TRUE to enable TIME feature, FALSE to disable

;
;	PRINT3 is THE file print utility for ZCPR2.  Installable by GENINS,
; PRINT3 provides a wide range of options for the user.  First, PRINT3 allows
; the user to employ wild cards and file name lists (lists of files separated
; by commas, like: file1,file2,file3,...).  Second, PRINT3 provides the
; following options:
;		E	Exact Mode
;				No heading appears, no line numbering,
;				no page numbering, tab expansion, form
;				feeds
;		F	File Name Toggle
;				The Heading Line can optionally contain
;				the name of the file
;		H@head@	Heading Text
;				The user may specify the text of the heading
;				to appear at the top of every page
;		I	Inspect Files
;				The user approves each file to be printed
;				before the printing process begins
;		L	Toggle Line Numbering
;				Each line may or may not begin with a line
;				number
;		M	Multiple Runs Toggle
;				The user may or may not be prompted to set
;				the Top of Form on his printer; Multiple Runs
;				ON means that he will not be prompted
;		N	Toggle Page Numbering
;				The numbering of each page is turned on or
;				off
;		Snnnn	Skip to Specified Page
;				Printing begins on the indicated page
;		T	Toggle Time Display
;				Time/Date information is optionally included
;				in the page header
;


;
;	This program is Copyright (c) 1982, 1983 by Richard Conn
;	All Rights Reserved
;
;	ZCPR2 and its utilities, including this one, are released
; to the public domain.  Anyone who wishes to USE them may do so with
; no strings attached.  The author assumes no responsibility or
; liability for the use of ZCPR2 and its utilities.
;
;	The author, Richard Conn, has sole rights to this program.
; ZCPR2 and its utilities may not be sold without the express,
; written permission of the author.
;

;
;  BASIC SYSLIB ROUTINES NEEDED BY TEMPLATE
;
ESIZE	EQU	16	; SIZE OF DIR ENTRY (FROM SYSLIB DIRF ROUTINE)

	EXT	DIRFS	; DIRECTORY PROCESSOR
	EXT	DIRPACK	; PACK DIRECTORY

	EXT	ZGPINS	; INIT BUFFERS
	EXT	ZFNAME	; FILE NAME PROCESSOR

	EXT	INITFCB	; INIT FCB
	EXT	RETUD	; RETURN CURRENT USER/DISK
	EXT	PUTUD	; SAVE CURRENT USER/DISK
	EXT	GETUD	; RESTORE CURRENT USER/DISK
	EXT	LOGUD	; LOG INTO USER/DISK
	EXT	PRINT	; PRINT STRING PTED TO BY RET ADR
	EXT	PADC	; PRINT A IN DEC
	EXT	COUT	; CONSOLE OUTPUT ROUTINE
	EXT	CONDIN	; CONDITIONAL INPUT ROUTINE
	EXT	CIN	; CONSOLE INPUT ROUTINE
	EXT	CAPS	; CAPITALIZE ROUTINE
	EXT	CRLF	; NEW LINE ROUTINE
	EXT	CLINE	; COMMAND LINE STRING SAVE ROUTINE
	EXT	CODEND	; CODE END COMPUTATION ROUTINE

	EXT	F$OPEN	; FILE OPEN
	EXT	F$READ	; BLOCK READ
	EXT	F$CLOSE	; FILE CLOSE

	EXT	EVAL10	; STRING TO BINARY CONVERSION
	EXT	LHLDC	; LST: HL AS DEC OUTPUT
	EXT	LPSTR	; LST: (HL) STRING OUTPUT
	EXT	LPRINT	; LST: STRING OUTPUT
	EXT	LCRLF	; LST: NEW LINE
	EXT	LOUT	; LST: OUTPUT
	EXT	MOVEB	; MOVEB ROUTINE

	if	timeok
	EXT	TIME	; TIME Library Module Routine
	endif

;
;  CP/M EQUATES
;
CPM	EQU	0	; WARM BOOT
BDOSE	EQU	CPM+5	; BDOS ENTRY
FCB	EQU	CPM+5CH	; FCB
TBUFF	EQU	CPM+80H	; INPUT LINE BUFFER
DEL	EQU	7FH	; <DEL>
CR	EQU	13	; <CR>
FF	EQU	12	; <FF>
LF	EQU	10	; <LF>
CTRLC	EQU	'C'-'@'	; ^C
CTRLG	EQU	'G'-'@'
CTRLH	EQU	'H'-'@'
CTRLI	EQU	'I'-'@'
CTRLX	EQU	'X'-'@'
CTRLZ	EQU	'Z'-'@'

;
;  OTHER EQUATES
;
EOLD	EQU	0FFH	; END OF LOAD DELIMITER

;
;  Branch to Start of Program
;
	JMP	START

;
;******************************************************************
;
;  SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format
;
;	This data block precisely defines the data format for
; initial features of a ZCPR2 system which are required for proper
; initialization of the ZCPR2-Specific Routines in SYSLIB.
;

;
;  EXTERNAL PATH DATA
;
EPAVAIL:
	DB	0FFH	; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES)
EPADR:
	DW	40H	; ADDRESS OF EXTERNAL PATH IF AVAILABLE

;
;  INTERNAL PATH DATA
;
INTPATH:
	DB	0,0	; DISK, USER FOR FIRST PATH ELEMENT
			; DISK = 1 FOR A, '$' FOR CURRENT
			; USER = NUMBER, '$' FOR CURRENT
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0	; DISK, USER FOR 8TH PATH ELEMENT
	DB	0	; END OF PATH

;
;  MULTIPLE COMMAND LINE BUFFER DATA
;
MCAVAIL:
	DB	0FFH	; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE?
MCADR:
	DW	0FF00H	; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE

;
;  DISK/USER LIMITS
;
MDISK:
	DB	4	; MAXIMUM NUMBER OF DISKS
MUSER:
	DB	31	; MAXIMUM USER NUMBER

;
;  FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK
;
DOK:
	DB	0FFH	; ALLOW DISK CHANGE? (0=NO, 0FFH=YES)
UOK:
	DB	0FFH	; ALLOW USER CHANGE? (0=NO, 0FFH=YES)

;
;  PRIVILEGED USER DATA
;
PUSER:
	DB	10	; BEGINNING OF PRIVILEGED USER AREAS
PPASS:
	DB	'chdir',0	; PASSWORD FOR MOVING INTO PRIV USER AREAS
	DS	41-($-PPASS)	; 40 CHARS MAX IN BUFFER + 1 for ending NULL

;
;  CURRENT USER/DISK INDICATOR
;
CINDIC:
	DB	'$'	; USUAL VALUE (FOR PATH EXPRESSIONS)

;
;  DMA ADDRESS FOR DISK TRANSFERS
;
DMADR:
	DW	80H	; TBUFF AREA

;
;  NAMED DIRECTORY INFORMATION
;
NDRADR:
	DW	00000H	; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY
NDNAMES:
	DB	64	; MAX NUMBER OF DIRECTORY NAMES
DNFILE:
	DB	'NAMES   '	; NAME OF DISK NAME FILE
	DB	'DIR'		; TYPE OF DISK NAME FILE

;
;  REQUIREMENTS FLAGS
;
EPREQD:
	DB	0FFH	; EXTERNAL PATH?
MCREQD:
	DB	000H	; MULTIPLE COMMAND LINE?
MXREQD:
	DB	0FFH	; MAX USER/DISK?
UDREQD:
	DB	000H	; ALLOW USER/DISK CHANGE?
PUREQD:
	DB	000H	; PRIVILEGED USER?
CDREQD:
	DB	0FFH	; CURRENT INDIC AND DMA?
NDREQD:
	DB	0FFH	; NAMED DIRECTORIES?
Z2CLASS:
	DB	11	; CLASS 11
	DB	'ZCPR2'
	DS	10	; RESERVED

;
;  END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA
;
;******************************************************************
;

;
;  **** Special Initial Value Area for GENINS
;
LWIDTH:
	DB	132	; WIDTH OF LINE
LTPP:
	DB	44	; LINES OF TEXT PER PAGE
LSPP:
	DB	5	; LINES TO SKIP PER PAGE
CWIDTH:
	DB	80	; WIDTH OF SCREEN
CTPP:
	DB	22	; LINES OF TEXT PER SCREEN
CSPP:
	DB	1	; LINES TO SKIP PER SCREEN
;
;  NOTE:  LTPP + LSPP + 2 (HEADER SIZE) = TOTAL LINES PER PAGE ON PRINTER
;  NOTE:  CTPP + CSPP + 1 (FOOTER SIZE) = TOTAL LINES PER SCREEN ON CONSOLE
;
DLNUMFL:
	DB	0	; LINE NUMBER FLAG (DEFAULT TO NO)
DPNUMFL:
	DB	0FFH	; PAGE NUMBER FLAG (DEFAULT TO YES)
DEXACT:
	DB	0	; EXACT PRINT FLAG (DEFAULT TO NO)
DFNPFL:
	DB	0FFH	; FILE NAME PRINT FLAG (DEFAULT TO YES)
DTIMEPFL:
	DB	0FFH	; TIME PRINT FLAG (DEFAULT TO YES)
DMULTFL:
	DB	0FFH	; MULTIPLE RUN FLAG (DEFAULT TO YES)
DINSPECT:
	DB	0	; INSPECT FILES (DEFAULT TO NO)
;
;  OTHER BUFFERS
;
SKIPFL:
	DB	0	; SKIP FLAG (DEFAULT TO NO)
SKIPNUM:
	DS	2	; PAGE NUMBER TO SKIP TO
LNUM:
	DS	2	; CURRENT LINE NUMBER
PNUM:
	DS	2	; CURRENT PAGE NUMBER
HBUF:
	DS	100	; BUFFER FOR HEADING
;
	if	timeok
TIMEBF:
	DS	100	; BUFFER FOR TIME STAMP
	endif
;
LNUMFL:
	DB	0	; LINE NUMBER FLAG (DEFAULT TO NO)
PNUMFL:
	DB	0FFH	; PAGE NUMBER FLAG (DEFAULT TO YES)
EXACT:
	DB	0	; EXACT PRINT FLAG (DEFAULT TO NO)
FNPFL:
	DB	0FFH	; FILE NAME PRINT FLAG (DEFAULT TO YES)
TIMEPFL:
	DB	0FFH	; TIME PRINT FLAG (DEFAULT TO YES)
MULTFL:
	DB	0FFH	; MULTIPLE RUN FLAG (DEFAULT TO YES)
INSPECT:
	DB	0	; INSPECT FILES (DEFAULT TO NO)

;
;  Start of Program
;
START:
	LXI	H,0	; GET STACK PTR
	DAD	SP
	SHLD	STACK	; SAVE IT
	LXI	SP,STACK	; SET SP
	CALL	PUTUD	; SAVE CURRENT USER/DISK AWAY
	CALL	RETUD	; GET CURRENT USER/DISK
	MOV	A,B	; SAVE DISK
	STA	DISK
	MOV	A,C	; SAVE USER
	STA	USER

	CALL	ZGPINS	; INIT BUFFERS

	LXI	H,TBUFF	; SAVE COMMAND LINE
	CALL	CLINE
	SHLD	CMDLNE	; SAVE PTR

;
;  **** Banner of Program
;
	CALL	PRINT
	DB	'PRINT III,  Version '
	DB	VERS/10+'0','.',(VERS MOD 10)+'0',0

;
;  Check for Help Request
;
	LDA	FCB+1	; GET FIRST CHAR OF FILE NAME
	CPI	' '	; NO FILE SPEC?
	JZ	HELP
	CPI	'/'	; OPTION CAUGHT?
	JNZ	ECONT

;
;  **** Print Help Information
;
HELP:
	CALL	PRINT
	DB	CR,LF,'PRINT III Command --'
	db	cr,lf,'	PRINT III is invoked by the following command line:'
	db	cr,lf,'		PRINT file1,file2,...,filen o...'
	db	cr,lf,'where each "filen" is an ambiguous file name and type'
	db	cr,lf,'and "o" is zero or more of the following options:'
	db	cr,lf,'	E	Exact Print (expand tabs, form feed, no line'
	db	cr,lf,'			or page numbers, no heading)'
	db	cr,lf,'	F	Toggle File Name Display'
	db	cr,lf,'	H@head@	Specify Page Heading (@ is any printing char)'
	db	cr,lf,'	I	Inspect and Select Files First'
	db	cr,lf,'	L	Toggle Numbering of Each Line'
	db	cr,lf,'	M	Toggle Multiple Runs (MR=No TOF Msg)'
	db	cr,lf,'	N	Toggle Numbering of Each Page'
	db	cr,lf,'	Snnnn	Skip to Specified Page before Printing'
;
	if	timeok
	db	cr,lf,'	T	Toggle Time Display (if available) in header'
	endif
;
	db	cr,lf,'Examples:'
	db	cr,lf,'		PRINT MYFILE.TXT,*.MAC LH''SAMPLE'''
	db	cr,lf,'			-- Number Lines, SAMPLE is Heading'
	db	cr,lf,'		PRINT MYFILE.* S25E'
	db	cr,lf,'			-- Skip to Page 25, Exact Print'
	db	cr,lf,'	At any time, ^C aborts PRINT III and ^X skips to next '
	db	'file'
	DB	CR,LF,0

;
;  RETURN TO OS
;
RETURN:
	LHLD	STACK	; GET OLD STACK
	SPHL		; SET IT
	RET

;
;  PROGRAM'S INIT ROUTINE
;
ECONT:
	CALL	INIT	; PROG INIT ROUTINE
;
;  EXTRACT FLAGS IF PRESENT
;
	LXI	H,0	; SET FILE COUNT
	SHLD	FILECNT
	LHLD	CMDLNE	; PT TO BUFFER
;
;  SKIP TO FILE NAME STRING
;
	CALL	SBLANK	; SKIP OVER BLANKS
;
;  SKIP TO END OF FILE NAME STRING
;
	CALL	SNBLANK	; SKIP OVER NON-BLANKS
;
;  CHECK FOR LEADING SLASH ON OPTION AND SKIP IT IF SO
;
OPT:
	CPI	'/'	; OPTION CHAR?
	JNZ	OPTION
	INX	H	; SKIP SLASH
;
;  PROCESS LIST OF OPTIONS
;
OPTION:
	MOV	A,M	; GET BYTE
	ORA	A	; DONE?
	JZ	DSPEC
	INX	H	; PT TO NEXT CHAR
	CPI	' '	; SKIP OVER SPACES
	JZ	OPTION
	MOV	C,A	; COMMAND IN C
	LXI	D,OPTAB	; PT TO OPTION TABLE
OPTL:
	LDAX	D	; GET OPTION LETTER
	ORA	A	; END OF TABLE?
	JZ	HELP	; HELP IF SO
	CMP	C	; MATCH?
	JZ	OPTM	; PROCESS IF SO
	INX	D	; PT TO NEXT ENTRY
	INX	D
	INX	D
	JMP	OPTL
;
;  PROCESS OPTION
;
OPTM:
	PUSH	H	; SAVE HL ON STACK
	LXI	H,OPTION	; GET RETURN ADDRESS
	XTHL		; ... ON STACK AND RESTORE HL
	INX	D	; PT TO ADDRESS
	LDAX	D	; GET ADDRESS LOW
	MOV	B,A	; ... IN B
	INX	D
	LDAX	D	; GET ADDRESS HIGH
	MOV	D,A	; ... IN D
	MOV	E,B	; LOW IN E
	PUSH	D	; PUT ADDRESS ON STACK
	MOV	A,C	; COMMAND IN A
	RET		; "CALL" OPTION ROUTINE

;
;  **** PROGRAM INIT ROUTINE
;	THIS ROUTINE IS USED BY THE PROGRAM TO PERFORM ANY INITS
;
INIT:
	lxi	h,dlnumfl	;copy defaults into buffers
	lxi	d,lnumfl
	mvi	b,7		;7 bytes
	call	moveb		;do copy
	xra	a	;A=0
	sta	skipfl	;set no skip
	sta	hbuf	;set no heading
;
	if	timeok
	call	time	;get time string
	lxi	d,timebf	;store in buffer
initt:
	mov	a,m	;get byte
	stax	d	;put byte
	inx	h	;pt to next
	inx	d
	ora	a	;done?
	jnz	initt
	endif
;
	RET

;
;  **** OPTION TABLE
;	EACH OPTION IS A CAPITAL LETTER OR SPECIAL CHAR FOLLOWED BY
;		AN ADDRESS; THE TABLE IS TERMINATED BY A BINARY ZERO
;
OPTAB:
	db	'E'
	dw	optexact
	db	'F'
	dw	optfn
	db	'H'
	dw	opthd
	db	'I'
	dw	optinsp
	db	'L'
	dw	optln
	db	'M'
	dw	optmult
	db	'N'
	dw	optpn
	db	'S'
	dw	optskip
;
	if	timeok
	db	'T'
	dw	opttime
	endif
;
	DB	0	; END OF TABLE
;
;  Option:  E (Toggle exact mode)
;
optexact:
	lda	exact	;get flag
	cma		;flip it
	sta	exact	;put flag
	ret
;
;  Option:  F (Toggle file name display)
;
optfn:
	lda	fnpfl	;get flag
	cma		;flip it
	sta	fnpfl	;put flag
	ret
;
;  Option:  H (Set Heading)
;
opthd:
	lxi	d,hbuf	;pt to heading buffer
	mov	a,m	;get delim
	ora	a	;none?
	rz
	mov	b,a	;delim in B
	inx	h	;pt to next char
opthd1:
	mov	a,m	;get next char
	ora	a	;done?
	jz	opthd3
	cmp	b	;done by trailing delim?
	jz	opthd2
	stax	d	;save char
	inx	h	;pt to next
	inx	d
	jmp	opthd1
opthd2:
	inx	h	;skip over delim
opthd3:
	xra	a	;store ending 0
	stax	d
	ret
;
;  Toggle Inspect Option
;
optinsp:
	lda	inspect	;flip flag
	cma
	sta	inspect
	ret
;
;  Set Line Number Flag
;
optln:
	lda	lnumfl	;flip flag
	cma
	sta	lnumfl
	ret
;
;  Set Multiple Run Flag
;
optmult:
	lda	multfl	;flip flag
	cma
	sta	multfl
	ret
;
;  Set Page Numbering Flag
;
optpn:
	lda	pnumfl	;flip flag
	cma
	sta	pnumfl
	ret
;
;  Set Skip Flag and get number
;
optskip:
	mvi	a,0ffh	;set flag
	sta	skipfl
	call	eval10	;get number
	xchg
	shld	skipnum	;set page number to skip to
	xchg		;HL pts to next char
	mov	a,d	;see if page number was zero
	ora	e
	jnz	option
	xra	a	;if zero, just turn off skip flag
	sta	skipfl
	ret
;
	if	timeok
;
;  Set Time Flag
;
opttime:
	lda	timepfl	;flip flag
	cma
	sta	timepfl
	ret
;
	endif
;
;  BEGIN MOVING THROUGH FILE NAMES, SEPARATED BY COMMAS
;
DSPEC:
	LHLD	CMDLNE	; PT TO FIRST BYTE
	CALL	SBLANK	; SKIP TO NON-BLANK
;
;  MAJOR REENTRY POINT WHEN FILE SPECS ARE SEPARATED BY COMMAS
;    HL PTS TO FIRST BYTE OF NEXT FILE SPEC
;
DSPEC1:
	LXI	SP,STACK	; RESET STACK
	CALL	GETUD	; RESET USER IF NECESSARY
	LXI	D,NTFCB	; PT TO FCB IN DE, PT TO FIRST CHAR OF FILE NAME IN HL

	CALL	ZFNAME	; EXTRACT FILE NAME INTO FCB, AND GET DISK AND USER
	JZ	DERR	; ERROR HANDLER

	SHLD	NEXTCH	; SAVE PTR TO DELIMITER WHICH ENDED SCAN
	MOV	A,B	; SAVE POSSIBLE DRIVE SPEC
	CPI	0FFH	; CURRENT DISK?
	JZ	DSPEC2
	LDA	MDISK	; GET MAX DISK NUMBER
	DCR	B	; ADJUST TO WITHIN BOUNDS 0-15
	CMP	B	; WITHIN BOUNDS?
	MOV	A,B	; GET DISK NUMBER IN A
	JNC	USPEC
DERR:
	CALL	PRINT
	DB	CR,LF,'Invalid Disk Specification',0
	JMP	DRETURN

;
;  SET CURRENT DISK
;
DSPEC2:
	LDA	DISK	;GET CURRENT DISK
	MOV	B,A	;... IN B

;  CHECK FOR USER NUMBER
USPEC:
	MOV	A,C	; GET NEW USER NUMBER
	CPI	0FFH	; DEFAULT USER?
	JZ	USPEC1
	CPI	'?'	; ALL USERS NOT ALLOWED?
	JZ	UERR
	LDA	MUSER	; GET MAX USER NUMBER
	CMP	C
	MOV	A,C	; USER NUMBER IN A
	JNC	FCT
UERR:
	CALL	PRINT
	DB	CR,LF,'Invalid User Number',0
	JMP	DRETURN
USPEC1:
	LDA	USER	;GET CURRENT USER
	MOV	C,A	;... IN C

;
;  LOAD DIRECTORY AND PERFORM FUNCTION
;
FCT:
	MOV	A,B	; SAVE NEW DISK/USER AWAY
	STA	CDISK	; CURRENT DISK
	MOV	A,C
	STA	CUSER	; CURRENT USER
	CALL	LOGUD	; LOG INTO ACCOUNT
	CALL	CODEND	; PT TO END OF CODE
	CALL	RETUD	; GET USER NUMBER FOR DIRFS
	MVI	A,11000000B	; SELECT SYS AND NON-SYS FILES
	ORA	C	; OR IN USER NUMBER
	LXI	D,NTFCB	; PT TO FCB
	CALL	INITFCB	; INIT THE FCB
	CALL	DIRFS	; LOAD DIR, SELECT FILES, PACK, AND ALPHABETIZE
;
;  DETERMINE BEGINNING OF SCRATCH AREA (SCRATCH) AND SZIE IN PAGES (BCNT)
;
	PUSH	H	; SAVE PTR AND COUNT
	PUSH	B
	LXI	D,ESIZE	; SET PTR TO NEXT FREE BLOCK
FCTFRE:
	MOV	A,B	; DONE?
	ORA	C
	JZ	FCTFR1
	DAD	D	; PT TO NEXT
	DCX	B	; COUNT DOWN
	JMP	FCTFRE
FCTFR1:
	INR	H	; NEXT PAGE
	MVI	L,0
	SHLD	SCRATCH	; SET PTR TO SCRATCH AREA
	XCHG		; PTR IN DE
	LHLD	BDOSE+1	; COMPUTE BLOCK BUFFER SIZE
	MOV	A,H	; ADJUST FOR ZCPR2
	SUI	10
	SUB	D	; A=SIZE IN BLOCKS
	STA	BCNT	; SET BLOCK COUNT
	POP	B	; RESTORE AND SAVE REGS
	POP	H
;
;  ALLOW USER TO INSPECT FILES
;
	PUSH	H
	PUSH	B
	CALL	ICHECK	; CHECK FOR INSPECT OPTION AND INSPECT IF SET
	POP	B	; RESTORE COUNT AND PTR
	POP	H

;
;  PERFORM FUNCTION; HL PTS TO FILE AND BC CONTAINS NUMBER OF FILES
;
FCTL:
	MOV	A,B		; CHECK FOR COMPLETION (COUNT = 0)
	ORA	C
	JZ	FCTL1
	DCX	B		; COUNT DOWN
	LXI	SP,STACK	; RESET STACK
	PUSH	B		; SAVE COUNT AND POINTER
	PUSH	H
	CALL	FUNCTION	; PERFORM FUNCTION
FCTLNXT:
	LXI	SP,STACK-4	; RESTORE STACK
	POP	H		; GET PTR AND COUNT
	POP	B
	LXI	D,ESIZE		; PT TO NEXT ENTRY
	DAD	D
	JMP	FCTL

;
;  CHECK FOR NEXT FILE SPEC
;
FCTL1:
	CALL	GETUD	; RETURN TO HOME USER/DISK
	LHLD	NEXTCH	; GET PTR
	MOV	A,M	; GET DELIM
	CPI	','	; ANOTHER FILE?
	JNZ	DRETURN
	INX	H	; PT TO CHAR AFTER COMMA
	JMP	DSPEC1	; CONTINUE PROCESSING

;
;  **** EMERGENCY ABORT
;
ABORT:
	CALL	PRINT
	DB	CR,LF,'** PRINT III Abort **',CR,LF,0
	CALL	GETUD	; RETURN HOME AND FALL THRU TO DRETURN
;
;  **** FUNCTION COMPLETE -- CLEANUP AND EXIT
;	FILL THIS IN WITH CLEANUP CODE FOR EXIT
;
DRETURN:
	JMP	RETURN

;
;  **** INSPECT FILES -- THIS ROUTINE IS TO PERFORM A FILE INSPECTION
;	ON INPUT, HL PTS TO FIRST 16-BYTE ENTRY AND BC=NUMBER OF ENTRIES
;
ICHECK:
	mov	a,b	;any files?
	ora	c
	rz
	push	h	;save ptrs
	push	b
	lxi	d,esize	;size of entry
ichk1:
	mvi	m,0	;clear MSBytes
	dad	d	;pt to next
	dcx	b	;count down
	mov	a,b	;done?
	ora	c
	jnz	ichk1
	pop	b	;restore ptrs
	pop	h
	lda	inspect	;inspect?
	ora	a	;0=no
	rz
	call	print
	db	cr,lf,'PRINT III File Inspect Mode',0
ichk2:
	call	print
	db	cr,lf,'Select ',0
	call	prfn	;print file name
	call	print
	db	' -- (Y/N/Q=Select Rest/S=Skip Rest/other=Y)? '
	db	0
	call	cin	;get response
	call	caps	;capitalize
	call	cout	;echo
	cpi	'Q'	;select rest?
	jz	ichkyr
	cpi	'S'	;skip rest
	jz	ichknr
	cpi	'N'	;no to this one?
	jnz	ichk3
	mvi	m,0ffh	;set NO flag in file FCB
ichk3:
	dad	d	;pt to next one
	dcx	b	;count down
	mov	a,b	;done?
	ora	c
	jnz	ichk2
	RET
;  Check Rest of Files as Selected
ichkyr:
	call	print
	db	cr,lf,'	Rest of Files Selected',0
	ret
;  Check Rest of Files as NOT Selected
ichknr:
	mvi	m,0ffh	;set NO flag
	dad	d	;pt to next
	dcx	b	;count down
	mov	a,b	;done?
	ora	c
	jnz	ichknr
	call	print
	db	cr,lf,'	Rest of Files NOT Selected',0
	ret
;
;  **** FUNCTION -- MAIN FUNCTION OF TEMPLATE
;	ON ENTRY, HL PTS TO NAME OF FILE (16 BYTES) AND USER IS LOGGED INTO
;		DIRECTORY CONTAINING INDICATED FILE
;
FUNCTION:
;
;  FILE PRINT Routine -- Print the File Whose Name is Pointed to by
;	HL; we are already logged into the correct directory
;
	mov	a,m	;file selected?
	ora	a	;0=yes
	rnz
	lda	multfl	;multiple runs?
	ora	a	;0=no
	jnz	fprint	;go right into function
	call	print
	db	cr,lf,'File: ',0
	call	prfn	;print file name
	call	print
	db	' -- Please Set Top of Form'
	db	cr,lf,'	Strike Any Key When Ready ',0
	call	cin	;get response
	cpi	ctrlc	;abort?
	jz	abort
	cpi	ctrlx
	rz
fprint:
	call	prinit	;init print buffers
	call	fload	;load buffer initially
	call	prhead	;print heading line
	lhld	scratch	;pt to first char in file
	shld	nxtln	;set pointer to next line
fprloop:
	call	prline	;print line of file
	jnz	fprloop	;done if EOF
	call	page	;advance to top of next page
	ret
;
;  Init Print Buffers and Print File Name
;
prinit:
	lxi	d,tfcb	;set up FCB
	mvi	b,12	;12 bytes
	call	moveb
	lxi	h,0	;HL=0
	shld	lnum	;set line number
	inx	h	;HL=1
	shld	pnum	;set page number
	lda	ltpp	;set line count
	sta	lcount
	call	print
	db	cr,lf,'Printing File ',0
	lxi	h,tfcb	;pt to FCB
	call	prfn	;print file name
	ret
;
;  FILE LOAD (FLOAD) Routine -- Initial Load of memory buffer
;
fload:
	lxi	d,tfcb	;pt to file fcb
	call	initfcb	;init file's fcb
	call	f$open	;open file for input
	jz	fload1	;open was OK
	call	print
	db	cr,lf,'File ',0
	xchg		;HL pts to FCB
	call	prfn	;print file name
	call	print
	db	' NOT Found',0
	pop	d	;clear return address
	ret		;abort printout of this file
;
;  This is an entry point for further memory loads of the file
;
fload1:
	lda	bcnt	;get number of blocks to load
	mov	c,a	;... in C
	lhld	scratch	;get address of first block to load into
	shld	nxtblk	;set pointer to next block to load
fload2:
	call	rdblk	;read a block (128 bytes)
	jnz	eof	;eof encountered?
	call	rdblk	;read another block (128 bytes)
	jnz	eof	;eof encountered?
	dcr	c	;count down
	jnz	fload2
	lhld	nxtblk	;pt to next byte to load
	mvi	m,eold	;mark end of load
	ret
eof:
	lxi	d,tfcb	;close file
	call	f$close
	lhld	nxtblk	;ensure ^Z
	mvi	m,ctrlz
	ret
rdblk:
	lxi	d,tfcb	;pt to FCB
	call	f$read	;read next block
	ora	a	;error?
	rnz
	lhld	nxtblk	;get ptr to next block
	xchg		; as dest
	lxi	h,tbuff	;ptr to DMA address
	mvi	b,128	;copy 128 bytes
rdblk1:
	mov	a,m	;get byte
	ani	7fh	;mask out msb
	stax	d	;put byte
	inx	h	;pt to next
	inx	d
	dcr	b	;count down
	jnz	rdblk1
	xchg		;new nxtblock
	shld	nxtblk
	ret

;
;  Line Print Routine
;	Print Next Line with Optional Disk Load
;	Input Parameter is NXTLN, which is the address of the first char
; on the next line
;	Output Parameter is Zero Flag, with Z meaning done with print, NZ
; meaning more yet to print
;
prline:
	lhld	lnum	;increment line number
	inx	h
	shld	lnum
	lhld	nxtln	;pt to first char of next line
	mvi	c,0	;init char count
	mov	a,m	;get first char of line
	cpi	ctrlz	;EOF?
	cnz	prlnum	;print line number (optional)
prl1:
	mov	a,m	;get char
	cpi	eold	;end of load?
	jz	prload
	cpi	ctrlz	;eof?
	jz	prexit
	inx	h	;pt to next char
	cpi	ctrli	;tab?
	jz	prtab
	cpi	cr	;<CR>?
	jz	prcr
	cpi	ff	;form feed?
	jz	prff
	cpi	lf	;end of line?
	jz	prldn
	cpi	ctrlh	;back space?
	jz	prbs
	cpi	ctrlg	;ring bell?
	jz	prbell
	cpi	del	;delete char?
	jz	prl1	;skip it
	cpi	' '	;other control char?
	jc	prl1	;skip if other control char
	call	prout	;print char
	inr	c	;increment char count
	call	eoltest	;check to see if at end of line and newline if so
	jmp	prl1
;
;  End of Load Reached -- Load More of File from Disk
;
prload:
	push	b	;save char count
	call	fload1	;use load routine
	pop	b	;get char count
	lhld	scratch	;next byte is here
	jmp	prl1	;continue processing
;
;  Tabulate
;
prtab:
	mvi	a,' '	;space
	call	prout
	inr	c	;new char
	call	eoltest	;process EOL
	mov	a,c	;done?
	ani	7
	jnz	prtab	;continue tabulation
	jmp	prl1	;continue processing
;
;  Exit with Zero Flag Set if Done
;
prexit:
	xra	a	;set zero flag
	ret
;
;  Carriage Return -- Reset Character Count and Continue
;
prcr:
	call	prout	;send CR to printer
	mvi	c,0	;reset char count
	jmp	prl1	;continue processing
;
;  Form Feed -- Advance to Top of Next Page
;
prff:
	call	page	;page eject with heading
	mvi	c,0	;reset char count
	jmp	prl1	;continue processing
;
;  Line Feed -- End of Routine
;
prldn:
	call	prout	;echo LF to printer
	shld	nxtln	;set ptr to first char of next line
	mvi	a,0ffh	;set not done
	ora	a	;set flags
	ret
;
;  Backspace on Printer
;
prbs:
	mov	a,c	;check for beginning of line
	ora	a
	jz	prl1	;continue if at BOL
	mvi	a,ctrlh	;backspace
	call	prout
	dcr	c	;back up char position
	jmp	prl1	;continue
;
;  Ring Bell on Printer
;
prbell:
	call	prout	;ring the bell
	jmp	prl1	;continue without advancing char position
;
;  Test for End of Line and Process if so
;
eoltest:
	lda	lwidth	;get line width
	sui	4	;4 chars less for continuation mark
	mov	b,a	;result in B
	lda	lnumfl	;line numbering (lines are 7 chars shorter if so)
	ora	a	;0=no
	jz	eolt1
	mov	a,b	;reduce by 7 for line numbers
	sui	7
	mov	b,a
eolt1:
	mov	a,b	;get line width
	cmp	c	;there?
	rnz		;continue if not
	mov	a,m	;get next char
	cpi	cr	;new line next?
	rz		;continue if so
	cpi	ctrlh	;backspace next?
	rz		;continue if so
	mvi	a,' '	;print continuation chars
	call	prout
	mvi	a,'<'
	call	prout
	mvi	a,'<'
	call	prout
	mvi	a,cr	;new line
	call	prout
	mvi	a,lf
	call	prout
	mvi	c,0	;reset char position
	lda	skipfl	;skipping?
	ora	a	;0=no
	rnz
	lda	lnumfl	;printing line numbers?
	ora	a	;0=no
	rz
	call	lprint
	db	'     : ',0
	ret
;
;  Output a character to the printer
;	A = Character
;
prout:
	mov	b,a	;char in B
	call	condin	;check for abort
	jz	prout1
	cpi	ctrlc	;abort?
	jz	abort
	cpi	ctrlx	;abort this one file?
	jz	cxabort
prout1:
	lda	skipfl	;skipping?
	ora	a	;set flags (Z=no skip=print char)
	mov	a,b	;restore char
	cz	lout	;send character to printer
	cpi	lf	;special tests if it is a line feed
	rnz		;done if non-LF char
	lda	lcount	;decrement line counter
	dcr	a
	sta	lcount
	rnz
;
;  Paging Required
;	Skip to top of next page; reset LCOUNT (Lines Left on Page Count);
;	print header
;
prout0:
	lda	ltpp	;get number of text lines per page
	sta	lcount	;set as new line count
	push	h	;save ptr
	lhld	pnum	;increment page number
	inx	h
	shld	pnum
	lda	lspp	;get number of lines to skip per page
	call	lineskp	;skip lines
	pop	h	;restore ptr
	mov	a,m	;check next character
	cpi	ctrlz	;EOF?
	cnz	prhead	;print 2-line heading if NOT EOF
	ret
;
;  Abort current file with final page eject
;
cxabort:
	lda	lcount	;get count of remaining lines
	call	lineskp	;skip lines
	lda	lspp	;number of lines to skip per page
	call	lineskp	;skip lines
	jmp	fctlnxt	;continue with next file
;
;  Skip out rest of page
;	Form Feed Function
;
page:
	lda	lcount	;get count of remaining lines
	call	lineskp	;skip lines
	jmp	prout0	;process top of new page
;
;  Skip out lines on page
;	A = number of lines to skip
;
lineskp:
	mov	b,a	;line count in B
	ora	a	;any?
	rz
	lda	skipfl	;skipping?
	ora	a
	rnz
lines1:
	mvi	a,cr	;output new line to printer
	call	lout
	mvi	a,lf
	call	lout
	dcr	b	;count down
	jnz	lines1
	ret
;
;  Print Line Number (optional)
;
prlnum:
	lda	skipfl	;skipping?
	ora	a	;0=no
	rnz
	lda	lnumfl	;get flag
	ora	a	;0=don't number lines
	rz
	push	h	;save ptr
	lhld	lnum	;get line number
	call	lhldc	;print line number
	call	lprint	;print separator
	db	': ',0
	pop	h	;restore ptr
	ret
;
;  Print 2-line heading and control skipping
;
prhead:
	push	h	;save ptr
	lda	skipfl	;currently skipping?
	ora	a	;0=no
	cnz	skiptst	;test for shut off
	lda	exact	;exact says no heading
	ora	a	;0FFH=yes
	jnz	prhead1
	lda	pnumfl	;number pages?
	ora	a	;0=no
	cnz	prpnum	;print page heading and number
	lda	fnpfl	;print file name?
	ora	a	;0=no
	cnz	prfname	;print file name
;
	if	timeok	;time available?
	lda	timepfl	;print time?
	ora	a	;0=no
	cnz	prtime	;print time
	endif
;
	lda	hbuf	;print heading?
	ora	a	;0=no
	cnz	prhdg	;print heading
prhead1:
	pop	h	;restore ptr
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	lcrlf	;new line
	jmp	lcrlf
;
;  Test for completion of skipping
;
skiptst:
	lhld	pnum	;get page number
	xchg		;... in DE
	lhld	skipnum	;get page to skip to
	mov	a,h	;compare them
	cmp	d
	rnz
	mov	a,l
	cmp	e
	rnz
	xra	a	;A=0 to stop skipping
	sta	skipfl	;set flag
	ret
;
;  Print Page Number
;
prpnum:
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	lprint	;print header
	db	'Page ',0
	lhld	pnum	;print current page number
	call	lhldc	;print as decimal
	ret
;
;  Print File Name
;
prfname:
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	prdash	;print separator
	lxi	h,tfcb+1	;pt to first char
	mvi	b,8	;8 chars
	call	lfn1
	mvi	a,'.'
	call	lout
	mvi	b,3	;3 chars
	call	lfn1
	ret
lfn1:
	mov	a,m	;get char
	ani	7fh	;mask
	call	lout	;send to printer
	inx	h	;pt to next
	dcr	b	;count down
	jnz	lfn1
	ret
;
;  Print Separator
;
prdash:
	call	lprint
	db	' -- ',0
	ret
;
	if	timeok
;
;  Print Time
;
prtime:
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	prdash	;print separator
	lxi	h,timebf	;pt to time stamp
	call	lpstr	;print
	ret
;
	endif
;
;  Print Header
;
prhdg:
	lda	skipfl	;skipping?
	ora	a
	rnz
	call	prdash	;print separator
	lxi	h,hbuf	;pt to heading
	call	lpstr	;print
	ret
;
;  UTILITIES
;	SBLANK  -- SKIP BLANKS PTED TO BY HL UNTIL NON-BLANK ENCOUNTERED; HL
;	SNBLANK -- SKIP NON-BLANKS PTED TO BY HL UNTIL BLANK OR EOL; HL
;	PRFN    -- PRINT FILE NAME PTED TO BY HL; AFFECT NOTHING
;

;
;  SKIP UNTIL NON-BLANK
;
SBLANK:
	MOV	A,M	; LOOK FOR BLANK
	INX	H	; PT TO NEXT
	CPI	' '	; BLANK?
	JZ	SBLANK
	DCX	H	; BACK UP
	RET

;
;  SKIP UNTIL BLANK OR EOL
;
SNBLANK:
	MOV	A,M	; GET CHAR
	INX	H	; PT TO NEXT
	CPI	' '	; BLANK?
	JZ	SNB1
	ORA	A	; EOL?
	JNZ	SNBLANK
SNB1:
	DCX	H	; BACK UP
	RET

;
;  PRINT FILE NAME PTED TO BY HL
;	OUTPUT TO CON:
;
PRFN:
	PUSH	H	; SAVE REGS
	PUSH	B
	CALL	RETUD	; GET CURRENT USER/DISK
	MOV	A,B	; PRINT DISK
	ADI	'A'	; LETTER
	CALL	COUT
	MOV	A,C	; PRINT USER
	CALL	PADC
	CALL	PRINT
	DB	': ',0
	INX	H	; PT TO FILE NAME
	MVI	B,8	; PRINT NAME
	CALL	PRNT
	MVI	A,'.'	; DECIMAL
	CALL	COUT
	MVI	B,3	; PRINT TYPE
	CALL	PRNT
	POP	B	; GET REGS
	POP	H
	RET

;
;  PRINT CHARS PTED TO BY HL FOR B BYTES
;	OUTPUT TO CON:
;
PRNT:
	MOV	A,M	; GET CHAR
	CALL	COUT
	INX	H	; PT TO NEXT
	DCR	B	; COUNT DOWN
	JNZ	PRNT
	RET

;
;  BUFFERS
;
DISK:
	DS	1	; HOME DISK NUMBER
USER:
	DS	1	; HOME USER NUMBER
CDISK:
	DS	1	; CURRENT DISK NUMBER
CUSER:
	DS	1	; CURRENT USER NUMBER
CMDLNE:
	DS	2	; PTR TO COMMAND LINE STRING
NEXTCH:
	DS	2	; PTR TO NEXT CHAR IN MULTIFILE COMMAND LINE
FILECNT:
	DS	2	; COUNT OF NUMBER OF FILES RENAMED
SCRATCH:
	DS	2	; ADDRESS OF FIRST FREE BYTE
BCNT:
	DS	1	; NUMBER OF PAGES IN SCRATCH AREA
NTFCB:
	DS	36	; FCB FOR NEW FILE
;
;  PRINT3 Buffers
;
tfcb:
	ds	36	; FCB for current file
nxtblk:
	ds	2	; pointer to next block to load
nxtln:
	ds	2	; pointer to next line to read
lcount:
	ds	1	; count of text lines left on page
;
;  Stack
;
	DS	100	; STACK AREA
STACK:
	DS	2	; OLD STACK PTR

	END
